using System;
using System.IO;
using System.Collections;
using System.Windows.Forms;

namespace Magic.Core
{
    /// <summary>
    /// Contains a list of loaded tags and references to their classes, for reusage.
    /// </summary>
    public class TagLibrary
    {
        Cxpe structs;
        ArrayList tags;
        ArrayList missingTags;
        ArrayList structureBsps;
        int modelIndexOffset;
        int modelVertexOffset;
        uint[] refStringOffsets;
        EndianReader bitmaps;
        EndianReader sounds;
        HaloMap.MapFormat format;

        public delegate void MessageEventHandler(string message);
        public event MessageEventHandler StatusMessage;


        public TagLibrary(Cxpe inStructure)
        {
            tags = new ArrayList();
            missingTags = new ArrayList();
            structureBsps = new ArrayList();
            structs = inStructure;
        }
        protected void OnMessage(string e)
        {
            if (StatusMessage != null)
                StatusMessage(e);
        }

        public int GetDependencyTag(string name, string type, string directory, string alias, bool explicitBsp, string ceTagsDirectory)
        {
            //Console.WriteLine(directory + '\\' + name + '.' + structs[type].Extension);

            if (type == "sbsp" && !explicitBsp)
            {
                for (int x = 0; x < structureBsps.Count; x++)
                    if (Convert.ToString(structureBsps[x]) == name)
                        return -1;
                structureBsps.Add(name);
                return -1;
            }
            if (type == "sbsp" && explicitBsp)
            {
                bool found = false;
                for (int x = 0; x < structureBsps.Count; x++)
                    if (Convert.ToString(structureBsps[x]) == name)
                        found = true;
                if (!found) { structureBsps.Add(name); }

            }
            for (int x = 0; x < tags.Count; x++)
            {
                TagIndexInstance cTag = tags[x] as TagIndexInstance;
                if (cTag.Name == name && cTag.Type == type)
                    return x;
            }
        Retry:
            if (type == "mod2")
                type = "mode";
            if (!File.Exists(directory + '\\' + name + '.' + structs[type].Extension))
            {
                string oType = type;
                if (oType == "mode")
                    type = "mod2";
                if (oType == "mod2")
                    type = "mode";
                if (oType == "scex")
                    type = "sotr";
                if (oType == "sotr")
                    type = "scex";
                if (!File.Exists(directory + '\\' + name + '.' + structs[type].Extension))
                {
                    if (oType == "scex")
                        type = "schi";
                    if (oType == "sotr")
                        type = "schi";
                    if (!File.Exists(directory + '\\' + name + '.' + structs[type].Extension))
                    {
                        if (File.Exists(ceTagsDirectory + '\\' + name + "." + structs[type].Extension))
                        {
                            string srcTag = ceTagsDirectory + '\\' + name + "." + structs[type].Extension;
                            string destTag = directory + '\\' + name + '.' + structs[type].Extension;
                            FileInfo destFile = new FileInfo(destTag);
                            if (!destFile.Directory.Exists)
                                destFile.Directory.Create();
                            File.Copy(srcTag, destTag);
                            goto Retry;
                        }
                        else
                        {
                            if (MessageBox.Show("Need to get the following tag: " + name + '.' + structs[oType].Extension, "Missing Tag", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
                            {
                                goto Retry;
                            }
                            else
                            {
                                foreach (string checkTag in missingTags)
                                    if (checkTag == name + '.' + structs[oType].Extension)
                                        return -1;
                                missingTags.Add(name + '.' + structs[oType].Extension);
                                return -1;
                            }
                        }

                    }
                }
            }
            else
            {
                if (type == "scex")
                {
                    if (File.Exists(directory + '\\' + name + '.' + structs["sotr"].Extension))
                        type = "sotr";
                    else if (File.Exists(directory + '\\' + name + '.' + structs["schi"].Extension))
                        type = "schi";
                }
            }

            OnMessage("Loading tag '" + name + '.' + structs[type].Extension + '\'');

            int curCount = tags.Count;
            TagIndexInstance instance = new TagIndexInstance();
            instance.ID = (uint)((curCount * 0x10001) + 0xe1740000);
            instance.Type = type;
            instance.Name = alias;
            tags.Add(instance);
            instance.Tag = new Tag(directory + '\\' + name + '.' + structs[type].Extension, directory, this, ceTagsDirectory, false);
            return curCount;
        }
        public int GetDependencyTag(string name, string type, string directory, bool explicitBsp, string ceTagsDirectory)
        {
            return GetDependencyTag(name, type, directory, name, explicitBsp, ceTagsDirectory);
        }
        public int GetDependencyTag(string name, string type, string directory, string ceTagsDirectory)
        {
            return GetDependencyTag(name, type, directory, name, false, ceTagsDirectory);
        }

        public Tag this[int index]
        {

            get
            {
                if (index == -1)
                { return null; }
                else { return (tags[index] as TagIndexInstance).Tag; }
            }
            set { (tags[index] as TagIndexInstance).Tag = value; }
        }

        public uint GetID(int index)
        {
            return (tags[index] as TagIndexInstance).ID;
        }

        public string GetName(int index)
        {
            if (index == -1) { return "<none>"; }
            return (tags[index] as TagIndexInstance).Name;
        }

        public string GetClass(int index)
        {
            return (tags[index] as TagIndexInstance).Type;
        }

        public uint IDByTag(Tag tag)
        {
            for (int x = 0; x < tags.Count; x++)
                if ((tags[x] as TagIndexInstance).Tag == tag)
                    return (tags[x] as TagIndexInstance).ID;
            return 0xffffffff;
        }

        public int IndexByName(string name, string type)
        {
            for (int x = 0; x < tags.Count; x++)
            {
                TagIndexInstance test = tags[x] as TagIndexInstance;
                if (test.Name == name)
                    if (test.Type == type)
                        return x;
            }
            return -1;
        }

        public int IndexByID(uint id)
        {
            for (int x = 0; x < tags.Count; x++)
                if ((tags[x] as TagIndexInstance).ID == id)
                    return x;
            return -1;
        }

        public int Count
        {
            get { return tags.Count; }
        }

        public void AddTag(string name, string type, uint id, Tag tag)
        {
            TagIndexInstance instance = new TagIndexInstance();
            instance.ID = id;//(uint)((tags.Count * 0x10001) + 0xe1740000);
            instance.Type = type;
            instance.Name = name;
            instance.Tag = tag;
            tags.Add(instance);
        }

        public void AssertAllTagsPresent()
        {
            if (missingTags.Count == 0)
                return;
            string message = "Need to get the following tags:\n";
            for (int x = 0; x < missingTags.Count; x++)
                message += missingTags[x] as string + '\n';
            message += "This application will now exit, allowing you some taskbar space to copy or make that stuff or whatever.";
            System.Windows.Forms.MessageBox.Show(message, "Missing Resources", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
            System.Windows.Forms.Application.Exit();
        }

        private class TagIndexInstance
        {
            public Tag Tag;
            public string Name;
            public string Type;
            public uint ID;
        }

        public Cxpe CXPE
        {
            get { return structs; }
        }

        public string[] StructureBsps
        {
            get { return structureBsps.ToArray(typeof(string)) as string[]; }
        }

        public uint[] StringOffsets
        {
            get { return refStringOffsets; }
            set { refStringOffsets = value; }
        }

        public HaloMap.MapFormat MapFormat
        {
            get { return format; }
            set { format = value; }
        }

        public EndianReader Sounds
        {
            get { return sounds; }
            set { sounds = value; }
        }

        public EndianReader Bitmaps
        {
            get { return bitmaps; }
            set { bitmaps = value; }
        }

        public int ModelIndexOffset
        {
            get { return modelIndexOffset; }
            set { modelIndexOffset = value; }
        }

        public int ModelVertexOffset
        {
            get { return modelVertexOffset; }
            set { modelVertexOffset = value; }
        }

        public void Refactor(int index, string newName)
        {
            (tags[index] as TagIndexInstance).Name = newName;
        }

        public void Recommission(int index, string newType)
        {
            (tags[index] as TagIndexInstance).Type = newType;
        }
    }
}
